package com.gin.kotlin.no_gay_alliance.activity

import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Message
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import androidx.activity.result.ActivityResultLauncher
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.gin.kotlin.no_gay_alliance.R
import com.gin.kotlin.no_gay_alliance.activity.MainActivity.Companion.handleCommonOptionsItemSelected
import com.gin.kotlin.no_gay_alliance.activity.PostActivity.Companion.obtainIntent
import com.gin.kotlin.no_gay_alliance.activity.PostActivity.PostResult
import com.gin.kotlin.no_gay_alliance.adapter.TopicBookmarkAdapter
import com.gin.kotlin.no_gay_alliance.adapter.reply.CustomReputationLevelAdapter
import com.gin.kotlin.no_gay_alliance.app.App
import com.gin.kotlin.no_gay_alliance.argument.PostPrepareArgument
import com.gin.kotlin.no_gay_alliance.argument.PostPrepareArgument.Action
import com.gin.kotlin.no_gay_alliance.argument.ReplyListArgument
import com.gin.kotlin.no_gay_alliance.argument.ReplyListArgument.Companion.MAX_PAGE
import com.gin.kotlin.no_gay_alliance.argument.ReplyListArgument.Type
import com.gin.kotlin.no_gay_alliance.argument.VoteArgument
import com.gin.kotlin.no_gay_alliance.base.AlertDialogUtils
import com.gin.kotlin.no_gay_alliance.base.ClipboardUtils
import com.gin.kotlin.no_gay_alliance.base.InputMethodUtils
import com.gin.kotlin.no_gay_alliance.base.IntRangeTextWatcher
import com.gin.kotlin.no_gay_alliance.callback.RCallback
import com.gin.kotlin.no_gay_alliance.database.dao.BanTopicDao
import com.gin.kotlin.no_gay_alliance.database.dao.TopicBookmarkDao
import com.gin.kotlin.no_gay_alliance.database.entity.BanTopic
import com.gin.kotlin.no_gay_alliance.database.entity.ReplyHistoryTask
import com.gin.kotlin.no_gay_alliance.database.entity.ReplyHistoryTask.Status
import com.gin.kotlin.no_gay_alliance.database.entity.ReplyHistoryTopic
import com.gin.kotlin.no_gay_alliance.database.entity.TopicBookmark
import com.gin.kotlin.no_gay_alliance.databinding.ActivityReplyListBinding
import com.gin.kotlin.no_gay_alliance.databinding.DialogPagerBinding
import com.gin.kotlin.no_gay_alliance.databinding.DialogReplyHistoryTaskBinding
import com.gin.kotlin.no_gay_alliance.enums.HandlerMessageWhat
import com.gin.kotlin.no_gay_alliance.fragment.replylist.AbstractReplyPageFragment
import com.gin.kotlin.no_gay_alliance.fragment.replylist.ReplyPageHtmlFragment
import com.gin.kotlin.no_gay_alliance.provider.ngaphp.FavorFolderProvider.Companion.showFolderDialog
import com.gin.kotlin.no_gay_alliance.provider.ngaphp.ReplyListProvider
import com.gin.kotlin.no_gay_alliance.store.GlobalProperty
import com.gin.kotlin.no_gay_alliance.webview.BbsCodeWebView
import com.gin.kotlin.no_gay_alliance.webview.BbsCodeWebViewPool
import com.gin.nga.callback.RedirectCallback
import com.gin.nga.enums.NgaPhpApi
import com.gin.nga.enums.VoteType
import com.gin.nga.response.reply.ReplyListBody
import com.gin.nga.utils.NgaLocation
import com.gin.nga.utils.NgaUrlUtils
import retrofit2.Call
import retrofit2.Response
import java.util.regex.Pattern

/**
 * 回复列表活动
 * @property currentFragment 当前回复页
 * @property topicBookmarkDao 主题书签dao
 * @property bookmarkAdapter 书签适配器
 * @property argument 参数
 * @property bbsCodeWebViewPool WebView池
 * @property flagJumpToEnd 是否跳转到底部的flag
 * @property total 总楼层数
 */
class ReplyListActivity : AbstractPagerListActivity<ActivityReplyListBinding>(), DrawerLayout.DrawerListener {
    private lateinit var banTopicDao: BanTopicDao
    private var currentFragment: AbstractReplyPageFragment? = null
    private lateinit var topicBookmarkDao: TopicBookmarkDao
    private lateinit var bookmarkAdapter: TopicBookmarkAdapter
    private lateinit var argument: ReplyListArgument
    lateinit var bbsCodeWebViewPool: BbsCodeWebViewPool
    private var flagJumpToEnd = false
    private var total = 1

    /**
     * 解析意图
     * @param intent Intent?
     */
    override fun handleIntent(intent: Intent) {
        this.argument = parseArgument(intent, KEY_ARGUMENT, ReplyListArgument::class.java) { ReplyListArgument.parseUrl(it) }

        // 如果参数携带了总页数，覆盖
        this.argument.totalPage?.also { updatePage(totalPage = this.totalPage.coerceAtLeast(it)) }
        // 如果携带了总楼层数，覆盖
        this.total = this.argument.total?.coerceAtLeast(this.total) ?: this.total
    }

    /**
     * 装载右上角菜单, 使用 [MenuInflater.inflate] 方法
     * @param menu Menu
     * @param menuInflater MenuInflater
     */
    override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
        //清空菜单
        menu.clear()

        //装载通用菜单 提醒、私信、主题浏览历史、举报记录、回复历史、收藏夹
        menuInflater.inflate(R.menu.common_menu, menu)
        // 复制代码、复制链接
        menuInflater.inflate(R.menu.copy, menu)
        // 只看我，只看楼主， 添加收藏，声望等级、添加观察、添加扫描、单帖屏蔽、取消屏蔽
        menuInflater.inflate(R.menu.reply_list, menu)

        // 单回复模式下才显示跳转到LIST模式按钮
        menu.findItem(R.id.mi_single_to_list).isVisible = argument.type == Type.SINGLE
        menu.findItem(R.id.mi_notice).setShowAsAction(if (argument.type == Type.SINGLE) MenuItem.SHOW_AS_ACTION_NEVER else MenuItem.SHOW_AS_ACTION_IF_ROOM)

    }

    override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
        val topic = currentFragment?.topic
        menu?.findItem(R.id.mi_vote)?.also {
            it.isVisible = topic?.postMiscVar?.getVoteData() != null
        }
        val banTopics = banTopicDao.list()
        val banned = banTopics?.any { it.topicId == topic?.topicId } == true
        menu?.findItem(R.id.mi_ban_topic)?.also { it.isVisible = !banned }
        menu?.findItem(R.id.mi_ban_topic_cancel)?.also { it.isVisible = banned }

        return super.onPrepareOptionsMenu(menu)
    }


    /**
     * 菜单响应
     * @param item MenuItem
     * @return Boolean
     */
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            // 单回复模式跳转到LIST模式
            R.id.mi_single_to_list -> startActivity(this, argument.copy(type = Type.LIST))
            R.id.mi_copy_link -> currentFragment?.topic?.topicId?.also {
                val text = NgaUrlUtils.build(GlobalProperty.getInstance().domain, NgaPhpApi.READ, Pair("tid", it.toString()))
                ClipboardUtils.setText(text)
                toast("已复制链接")
                Log.i(TAG, "已复制: $text")
            }

            R.id.mi_copy_code -> currentFragment?.topic?.getBbsCode()?.also {
                ClipboardUtils.setText(it)
                toast("已复制代码")
                Log.i(TAG, "已复制: $it")
            }

            R.id.mi_go_floor -> goFloor(this, total) { targetFloor ->
                Log.i(TAG, "跳转到楼层: $targetFloor")
                argument = argument.copy(targetPage = targetFloor / 20 + 1, targetFloor = targetFloor)
                configViewPager2()
//                startActivity(this, argument.copy(targetPage = targetFloor / 20 + 1, targetFloor = targetFloor))
            }

            R.id.mi_only_me -> startActivity(
                context = this,
                ReplyListArgument(Type.LIST, userId = App.INSTANCE.account.userId, topicId = currentFragment?.topic?.topicId, title = getString(R.string.mi_only_me))
            )

            R.id.mi_only_author -> currentFragment?.topic?.authorUid?.also {
                startActivity(
                    context = this,
                    ReplyListArgument(Type.LIST, userId = it.toLongOrNull(), topicId = currentFragment?.topic?.topicId, title = getString(R.string.mi_only_author))
                )
            }
            // 收藏主题
            R.id.mi_add_favor_topic -> showFolderDialog(this, argument.topicId!!)
            // 自定义声望等级
            R.id.mi_reputation_level -> currentFragment?.levels?.also { list ->
                val recyclerView = RecyclerView(this).also { rv ->
                    rv.layoutManager = LinearLayoutManager(this)
                    rv.adapter = CustomReputationLevelAdapter(this, handler!!, list.toMutableList())
                }
                AlertDialogUtils.builder(this, contentView = recyclerView).show()
            }
            //  添加到观察主题
            R.id.mi_add_observed -> {
                App.INSTANCE.database.replyHistoryTopicDao().add(ReplyHistoryTopic(argument.topicId!!, toolbar?.title.toString(), 0))
                toast("已添加")
            }
            //  添加回复历史任务
            R.id.mi_add_task -> addReplyHistoryTask()
            // 屏蔽本帖
            R.id.mi_ban_topic -> currentFragment?.topic?.also {
                banTopicDao.add(BanTopic(it.getEntranceId()!!, it.title))
                toast("已屏蔽")
            }
            // 解除屏蔽
            R.id.mi_ban_topic_cancel -> currentFragment?.topic?.getEntranceId()?.also {
                banTopicDao.del(it)
                toast("屏蔽已解除")
            }
            // 打开书签抽屉
            R.id.mi_bookmark_query -> b?.drawerLayout?.openDrawer(GravityCompat.END)

            R.id.mi_vote -> {
                currentFragment?.topic?.also { topic ->
                    when (topic.postMiscVar?.getVoteData()?.type) {
                        VoteType.SCORE_TOPIC, VoteType.VOTE_TOPIC, VoteType.BET_TOPIC -> VoteActivity.start(this, VoteArgument(topic))
                        else -> {}
                    }
                }
            }

            else -> handleCommonOptionsItemSelected(this, handler, item)
        }
        return true
    }

    /**
     * 添加回复历史任务
     */
    private fun addReplyHistoryTask() {
        val binding = DialogReplyHistoryTaskBinding.inflate(layoutInflater)
        binding.etTitle.setText(currentFragment?.topic?.title ?: "")
        binding.etStart.addTextChangedListener(IntRangeTextWatcher(binding.etStart, 1, MAX_PAGE))
        binding.etEnd.addTextChangedListener(IntRangeTextWatcher(binding.etEnd, 1, MAX_PAGE))
        //页数范围显示隐藏
        binding.spinnerType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val visible = if (position == 0) View.VISIBLE else View.GONE
                listOf(binding.tvLabelPageRange, binding.etStart, binding.etEnd).forEach { it.visibility = visible }
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {
            }
        }

        AlertDialogUtils.builder(this, "添加回复历史任务", contentView = binding.root, listener = object : DialogInterface.OnClickListener {
            override fun onClick(dialog: DialogInterface?, which: Int) {
                if (which == DialogInterface.BUTTON_POSITIVE) {
                    val topicId = currentFragment?.topic?.topicId ?: return
                    val task = ReplyHistoryTask(
                        topicId = topicId,
                        title = binding.etTitle.text.toString(),
                        remark = binding.etRemark.text.toString(),
                        type = if (binding.spinnerType.selectedItemPosition == 0) ReplyHistoryTask.Type.SCANNER else ReplyHistoryTask.Type.MONITOR,
                        status = if (binding.spinnerType.selectedItemPosition == 0) Status.WAITING else Status.IN_PROCESS,
                        start = binding.etStart.text.toString().toIntOrNull() ?: 0,
                        end = binding.etEnd.text.toString().toIntOrNull() ?: 0
                    )
                    App.INSTANCE.database.replyHistoryTaskDao().add(task)
                    toast("任务已添加到队列中")
                }
            }
        }).show()
    }

    override fun onCreate() {
        this.topicBookmarkDao = App.INSTANCE.database.topicBookmarkDao()
        this.banTopicDao = App.INSTANCE.database.banTopicDao()
        // 构建缓存
        BbsCodeWebView(this)
        // 如果回复ID非空主题ID为空，表示为to模式，使用to请求获取tid和页数
        if (this.argument.type == Type.LIST && this.argument.topicId == null && this.argument.replyId != null) {
            // to模式，请求重定向
            requestLocation(this.argument.replyId!!)
        } else if (this.argument.targetPage != null && this.argument.targetPage!! >= MAX_PAGE) {
            // 最后一页模式，请求获取正确的最大页
            ReplyListProvider.getInstance().enqueue(this.argument.copy(html = true), callback = object : RCallback<ReplyListBody, ReplyListArgument> {
                override fun onSuccess(data: ReplyListBody, ignoreCache: Boolean) {
                    // 如果获取到的最大页小于原指定页，设置获取到的页为目标页
                    data.totalPage?.also {
                        if (argument.targetPage!! >= it) {
                            argument = argument.copy(totalPage = it, targetPage = it)
                        }
                    }
                    ui { configViewPager2() }
                }

                override fun onError(errorMessage: String) {
                    toast(errorMessage)
                }
            })
        } else
            configViewPager2()

        // 正文WebView池
        this.bbsCodeWebViewPool = BbsCodeWebViewPool(this)


        this.bookmarkAdapter = TopicBookmarkAdapter(this, handler!!)
        this.b?.recyclerViewTopicBookmark?.adapter = this.bookmarkAdapter
        this.b?.drawerLayout?.addDrawerListener(this)
        this.b?.ivDeleteBookmark?.setOnClickListener {
            AlertDialogUtils.builder(this, "确认清空书签?", listener = { _, which ->
                if (which == DialogInterface.BUTTON_POSITIVE) {
                    topicBookmarkDao.clear(argument.topicId!!)
                    this.bookmarkAdapter.clear()
                }
            }).show()
        }
    }

    /**
     * 配置适配器
     */
    private fun configViewPager2() {
        this.b?.viewPager2?.adapter = object : FragmentStateAdapter(this) {
            override fun getItemCount() = totalPage
            override fun createFragment(position: Int) = ReplyPageHtmlFragment.newInstance(argument.copy(page = position + 1))
        }
        // 如果指定了页数，延迟翻页
        this.argument.targetPage?.also {
            updatePage(it, this.totalPage.coerceAtLeast(it))
            b?.viewPager2?.setCurrentItem(it - 1, false)
            Log.i(TAG, "翻页: $it")
        }
    }

    private fun requestLocation(replyId: Long) {
        App.INSTANCE.ngaApi.topicReadApi.toReply(replyId).enqueue(object : RedirectCallback() {
            override fun onRedirect(url: String, location: NgaLocation, call: Call<NgaLocation>, response: Response<NgaLocation>) {
                argument = argument.copy(
                    topicId = location.topicId ?: argument.topicId,
                    replyId = location.replyId ?: argument.replyId,
                    targetPage = location.page?.toIntOrNull() ?: argument.targetPage,
                )
                Log.i(TAG, "更新:回复列表参数: ${mapper.writeValueAsString(argument)}")

                ui { configViewPager2() }
            }

            override fun onFailure(call: Call<NgaLocation>, t: Throwable) {
                t.printStackTrace()
            }
        })
    }

    /**
     * 当fragment可见时设置其为当前Fragment
     * @param fragment Fragment?
     */
    fun onFragmentVisible(fragment: AbstractReplyPageFragment) {
        this.currentFragment = fragment
    }

    override fun onResume() {
        super.onResume()
        // 设置临时标题
        this.argument.title?.also { setToolbarTitle(it.replace(TITLE_PATTERN.toRegex(), "")) }
    }

    override fun handleMessage(msg: Message): Boolean {
        super.handleMessage(msg)
        if (msg.what == HandlerMessageWhat.PAGE_TO_TOPIC_BOOKMARK.ordinal) {
            val topicBookmark = msg.obj as? TopicBookmark ?: return false
            argument = argument.copy(userId = topicBookmark.userId, targetPage = topicBookmark.page, targetFloor = topicBookmark.floorNumber)
            configViewPager2()
            this.b?.drawerLayout?.closeDrawer(GravityCompat.END)
            return true
        }


        if (msg.what == HandlerMessageWhat.PAGE_UPDATED.ordinal) {
            Log.i(TAG, "参数信息更新: ${msg.arg1}/${msg.arg2}")
            // 更新参数中的最大页
            this.argument = this.argument.copy(totalPage = this.totalPage)
            // 检查是否要跳转到最新页
            if (msg.arg2 > 0) checkFlagJumpToEnd()
            supportInvalidateOptionsMenu()
            return true
        }

        if (msg.what == HandlerMessageWhat.TOTAL_UPDATED.ordinal) {
            Log.i(TAG, "楼层信息更新: ${msg.arg1}/${msg.arg2}")
            this.total = msg.arg1.coerceAtLeast(this.total)
            updatePage(totalPage = msg.arg2.takeIf { it > this.totalPage })

            this.argument = this.argument.copy(totalPage = this.totalPage, total = this.total)
            return true
        }

        // 回复、引用发帖成功
        if (msg.what == HandlerMessageWhat.POST_SUCCESS.ordinal) {
            val postResult = msg.obj as? PostResult ?: return true
            handlePostResult(postResult)
        }

        return super.handleMessage(msg)
    }

    override fun launchPostActivity(launcher: ActivityResultLauncher<Intent>) {
        val topicId = argument.topicId?.takeIf { it > 0 } ?: currentFragment?.topic?.topicId
        launcher.launch(obtainIntent(this, PostPrepareArgument(Action.REPLY, topicId = topicId)))
    }

    override fun refresh() {
        currentFragment?.refresh()
    }

    /**
     * 处理发帖结果
     * @param postResult PostResult
     */
    override fun handlePostResult(postResult: PostResult?) {
        // 设置标志
        flagJumpToEnd = true
        // 刷新
        currentFragment?.refresh()
    }

    /**
     * 检查标志： flagJumpToEnd，如果为true，设置为false并跳转到最后一页
     */
    private fun checkFlagJumpToEnd() {
        if (flagJumpToEnd) {
            Log.i(TAG, "跳转到最新页: $totalPage")
            flagJumpToEnd = false
            b?.viewPager2?.setCurrentItem(totalPage - 1, false)
        }
    }

    override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
    }

    override fun onDrawerOpened(drawerView: View) {
        // 查询书签
        argument.topicId?.also {
            val list = topicBookmarkDao.queryByTopicId(it)
            this.bookmarkAdapter.replaceData(list)
        }
    }

    override fun onDrawerClosed(drawerView: View) {
    }

    override fun onDrawerStateChanged(newState: Int) {
    }

    companion object {
        private val TITLE_PATTERN = Pattern.compile("\\[.+?]")
        const val KEY_ARGUMENT = "ARGUMENT_REPLY_LIST"

        /**
         * 弹窗选择跳转到楼层
         * @param context Context
         * @param total Int
         * @param onSet Function1<[@kotlin.ParameterName] Int, Unit>
         */
        fun goFloor(context: Context, total: Int, onSet: (floorNumber: Int) -> Unit) {
            val pagerBinding = DialogPagerBinding.inflate(LayoutInflater.from(context))
            // 设置初始值和 TextWatcher
            val editText = pagerBinding.editText
            editText.setText("1")
            editText.addTextChangedListener(IntRangeTextWatcher(editText, 1, total - 1))
            editText.requestFocus()
            editText.setSelection(0, editText.text.length)
            pagerBinding.tvTipsSelectPage.text = context.getString(R.string.tv_tips_go_floor, (total - 1).toString())

            AlertDialogUtils.builder(context, contentView = pagerBinding.root, listener = { _, which ->
                // 目标页数
                val num = when (which) {
                    DialogInterface.BUTTON_POSITIVE -> editText.text.toString().toInt()
                    else -> throw RuntimeException("非法输入")
                }
                onSet(num)
            }).show()

            // 延时激活输入法
            App.INSTANCE.handler?.postDelayed({ InputMethodUtils.show(editText) }, 300)
        }


        /**
         * 使用参数启动活动
         * @param context Context
         * @param argument TopicListArgument
         */
        fun startActivity(context: Context, argument: ReplyListArgument) {
            context.startActivity(Intent(context, ReplyListActivity::class.java).also { it.putExtra(KEY_ARGUMENT, argument) })
        }

        /**
         * 使用URL启动活动
         * @param context Context
         * @param url String
         */
        fun startActivity(context: Context, url: String) {
            context.startActivity(Intent(context, ReplyListActivity::class.java).also { it.setData(Uri.parse(url)) })
        }
    }
}